﻿using System.Runtime.InteropServices;
using ch07.binary.type;

namespace ch07.binary.interpreter
{
    using MemType = Limits;

    public struct Memory
    {
        public MemType Type;
        public byte[] Data;

        public Memory(MemType mt)
        {
            Type = mt;
            Data = new byte[mt.Min * ConstData.PageSize];
        }

        internal uint Size()
        {
            return (uint) (Data.Length / ConstData.PageSize);
        }

        internal uint Grow(uint n)
        {
            var oldSize = Size();
            if (n == 0)
            {
                return oldSize;
            }

            var maxPageCount = (uint) (ConstData.MaxPageCount);
            var max = Type.Max;
            if (max > 0)
            {
                maxPageCount = max;
            }

            if (oldSize + n > maxPageCount)
            {
                return 0xFFFFFFFF; // -1
            }

            var newData = new byte[(oldSize + n) * ConstData.PageSize];
            Copy(newData, Data);
            Data = newData;
            return oldSize;
        }

        private static void Copy(ref byte[] dst, ulong dstOffSet, ref byte[] src, ulong srcOffSet)
        {
            
            var srcLength = (ulong) src.Length - srcOffSet;
            var dstLength = (ulong) dst.Length - dstOffSet;
            for (var i = 0L; i < System.Math.Min((long) srcLength, (long) dstLength); i++)
            {
                dst[i + (long) dstOffSet] = src[i + (long) srcOffSet];
            }
        }

        private static void Copy(byte[] dst, byte[] src)
        {
            Copy(ref dst, 0UL, ref src, 0UL);
        }

        public void Read(ulong offset, byte[] buf)
        {
            CheckOffset(offset, buf.Length);
            Copy(ref buf, 0UL, ref Data, offset);
        }

        internal void Write(ulong offset, byte[] data)
        {
            CheckOffset(offset, data.Length);
            Copy(ref Data, offset, ref data, 0UL);
        }

        private void CheckOffset(ulong offset, int length)
        {
            if (Data.Length - length < (long) (offset))
            {
                Panic.Run(Errors.ErrMemOutOfBounds);
            }
        }
    }
}